﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using IronCow.Search;

namespace IronCow.UnitTests
{
    [TestFixture]
    public class TextLexicalAnalyzerTreeBuilding
    {
        public LexicalAnalyzer Lexer { get; private set; }

        [SetUp]
        public void SetUp()
        {
            Lexer = new LexicalAnalyzer();
        }

        [Test]
        public void TestWord()
        {
            var actual = Lexer.BuildAst(new List<Token>()
            {
                new Token("word")
            });
            var expected = new ParenthesisGroupNode()
            {
                new TermNode("word")
            };

            AssertExtensions.AreNodeEqual(expected, actual);
        }

        [Test]
        public void TestBoolean()
        {
            var actual = Lexer.BuildAst(new List<Token>()
            {
                new Token("one"),
                new Token(TokenType.BooleanAnd),
                new Token("two")
            });
            var expected = new ParenthesisGroupNode()
            {
                new BooleanNode(BooleanType.And)
                {
                    LeftChild = new TermNode("one"),
                    RightChild = new TermNode("two")
                }
            };

            AssertExtensions.AreNodeEqual(expected, actual);
        }

        [Test]
        public void TestNot()
        {
            var actual = Lexer.BuildAst(new List<Token>()
            {
                new Token("one"),
                new Token(TokenType.BooleanAnd),
                new Token(TokenType.UnaryNot),
                new Token("two")
            });
            var expected = new ParenthesisGroupNode()
            {
                new BooleanNode(BooleanType.And)
                {
                    LeftChild = new TermNode("one"),
                    RightChild = new UnaryNode(UnaryType.Not)
                    {
                        Child = new TermNode("two")
                    }
                }
            };

            AssertExtensions.AreNodeEqual(expected, actual);
        }

        [Test]
        public void TestParenthesisAndBoolean()
        {
            var actual = Lexer.BuildAst(new List<Token>()
            {
                new Token("one"),
                new Token(TokenType.BooleanAnd),
                new Token(TokenType.ParenthesisOpen),
                new Token("two"),
                new Token(TokenType.BooleanOr),
                new Token("three"),
                new Token(TokenType.ParenthesisClose)
            });
            var expected = new ParenthesisGroupNode()
            {
                new BooleanNode(BooleanType.And)
                {
                    LeftChild = new TermNode("one"),
                    RightChild = new ParenthesisGroupNode()
                    {
                        new BooleanNode(BooleanType.Or)
                        {
                            LeftChild = new TermNode("two"),
                            RightChild = new TermNode("three")
                        }
                    }
                }
            };

            AssertExtensions.AreNodeEqual(expected, actual);
        }

        [Test]
        public void TestOperator()
        {
            var actual = Lexer.BuildAst(new List<Token>()
            {
                new Token(TokenType.Operator, "due"),
                new Token("next week")
            });
            var expected = new ParenthesisGroupNode()
            {
                new OperatorNode("due", "next week")
            };

            AssertExtensions.AreNodeEqual(expected, actual);
        }

        [Test]
        public void TestBooleanAndOperator()
        {
            var actual = Lexer.BuildAst(new List<Token>()
            {
                new Token("one"),
                new Token(TokenType.BooleanAnd),
                new Token(TokenType.Operator, "due"),
                new Token("next week")
            });
            var expected = new ParenthesisGroupNode()
            {
                new BooleanNode(BooleanType.And)
                {
                    LeftChild = new TermNode("one"),
                    RightChild = new OperatorNode("due", "next week")
                }
            };

            AssertExtensions.AreNodeEqual(expected, actual);
        }

        [Test]
        public void TestBooleanAndTwoOperators()
        {
            var actual = Lexer.BuildAst(new List<Token>()
            {
                new Token(TokenType.Operator, "due"),
                new Token("next week"),
                new Token(TokenType.BooleanOr),
                new Token(TokenType.Operator, "tag"),
                new Token("todo")
            });
            var expected = new ParenthesisGroupNode()
            {
                new BooleanNode(BooleanType.Or)
                {
                    LeftChild = new OperatorNode("due", "next week"),
                    RightChild = new OperatorNode("tag", "todo")
                }
            };

            AssertExtensions.AreNodeEqual(expected, actual);
        }

        [Test]
        public void TestBooleanAndOperatorAndParenthesis()
        {
            var actual = Lexer.BuildAst(new List<Token>()
            {
                new Token("one"),
                new Token(TokenType.BooleanAnd),
                new Token(TokenType.ParenthesisOpen),
                new Token(TokenType.Operator, "due"),
                new Token("next week"),
                new Token(TokenType.BooleanOr),
                new Token(TokenType.Operator, "tag"),
                new Token("todo"),
                new Token(TokenType.ParenthesisClose)
            });
            var expected = new ParenthesisGroupNode()
            {
                new BooleanNode(BooleanType.And)
                {
                    LeftChild = new TermNode("one"),
                    RightChild = new ParenthesisGroupNode()
                    {
                        new BooleanNode(BooleanType.Or)
                        {
                            LeftChild = new OperatorNode("due", "next week"),
                            RightChild = new OperatorNode("tag", "todo")
                        }
                    }
                }
            };

            AssertExtensions.AreNodeEqual(expected, actual);
        }

        [Test]
        public void TestComplex1()
        {
            var actual = Lexer.BuildAst(new List<Token>()
            {
                new Token(TokenType.ParenthesisOpen),
                new Token(TokenType.Operator, "status"),
                new Token("incomplete"),
                new Token(TokenType.BooleanAnd),
                new Token(TokenType.ParenthesisOpen),
                new Token(TokenType.Operator,"tag"),
                new Token("next"),
                new Token(TokenType.BooleanOr),
                new Token(TokenType.Operator,"dueBefore"),
                new Token("tomorrow"),
                new Token(TokenType.ParenthesisClose),
                new Token(TokenType.ParenthesisClose)
            });
            var expected = new ParenthesisGroupNode()
            {
                new ParenthesisGroupNode()
                {
                    new BooleanNode(BooleanType.And)
                    {
                        LeftChild = new OperatorNode("status", "incomplete"),
                        RightChild = new ParenthesisGroupNode()
                        {
                            new BooleanNode(BooleanType.Or)
                            {
                                LeftChild = new OperatorNode("tag", "next"),
                                RightChild = new OperatorNode("dueBefore", "tomorrow")
                            }
                        }
                    }
                }
            };

            AssertExtensions.AreNodeEqual(expected, actual);
        }
    }
}
